
Type MaxMod_Instrument
 
	Field Name:String								' name of this instrument
	Field InstrumentType:Int						' always zero?

	Field SampleCount		:Int					' number of samples in instrument
	Field Sample			:MaxMod_Sample[128]		' array of samples
	Field NoteSample		:Int[100]				' index of sample to play for each note

	Field VolType			:Int					' Volume Type (bit 0: On; 1: Sustain; 2: Loop)
	Field VolPoints			:Int					' Number of volume points
	Field VolEnvelope		:Int[50]				' contains volume envelope points
	Field VolSustain		:Int					' Volume sustain point
	Field VolFadeout		:Int					' Volume fadeout
	Field VolLoopStart		:Int					' Volume loop start point
	Field VolLoopEnd		:Int					' Volume loop End point

	Field VolEnvList		:TList	= CreateList()
	Field VolEnvArray		:Object[0]
	Field VolSustainPoint	:MaxMod_EnvelopePoint
	Field VolLoopStartPoint	:MaxMod_EnvelopePoint
	Field VolLoopEndPoint	:MaxMod_EnvelopePoint

	Field PanType			:Int					' Panning Type
	Field PanPoints			:Int					' Number of panning points
	Field PanEnvelope		:Int[49]				' array containing panning envelope points	
	Field PanSustain		:Int					' Panning sustain point
	Field PanLoopStart		:Int					' Panning loop start point
	Field PanLoopEnd		:Int					' Panning loop End point

	Field VibType			:Int					' Vibrato Type
	Field VibSweep			:Int					' Vibrato sweep
	Field VibDepth			:Int					' Vibrato depth
	Field VibRate			:Int					' Vibrato rate

	Field Reserved			:Int

' -----------------------------------------------------------------------------------------------------------

	Function Load:MaxMod_Instrument(f:TStream)
		Local This:MaxMod_Instrument = New MaxMod_Instrument
		Local i:Int 		= ReadByte(f)
		This.Name 			= ReadString(f,i)
'		Print "loading instrument ~q"+This.Name+"~q"

		
		This.SampleCount 	= ReadShort(f)
		For i=0 Until This.SampleCount ; This.NoteSample[i] = ReadShort(f) ; Next

		This.VolType 		= ReadInt(f)
		This.VolPoints 		= ReadInt(f)
		Local l:Int 		= ReadInt(f)
		For i=0 Until l ; This.VolEnvelope[i] = ReadInt(f) ; Next
		This.VolSustain 	= ReadInt(f)
		This.VolFadeout 	= ReadInt(f)
		This.VolLoopStart 	= ReadInt(f)
		This.VolLoopEnd 	= ReadInt(f)
		
		Local ec:Int = ReadInt(f)
		This.VolEnvArray = New Object[ec]
		For i=0 Until ec
			This.VolEnvArray[i] = MaxMod_EnvelopePoint.Load(f)
		Next
		
'		WriteInt f,VolSustainPoint
'		WriteInt f,VolLoopStartPoint
'		WriteInt f,VolLoopEndPoint
		
		This.PanType 		= ReadInt(f)
		This.PanPoints 		= ReadInt(f)
		l = ReadInt(f)
		For i=0 Until l ; This.PanEnvelope[i] = ReadInt(f) ; Next
		This.PanSustain = ReadInt(f)
		This.PanLoopStart = ReadInt(f)
		This.PanLoopEnd = ReadInt(f)
		
		This.VibType = ReadInt(f)
		This.VibSweep = ReadInt(f)
		This.VibDepth = ReadInt(f)
		This.VibRate = ReadInt(f)
		
		This.Reserved = ReadInt(f)
		
		Local s:Int, sl:Int = ReadInt(f)
		For i=0 Until sl
			s = ReadByte(f)
			If S
				This.Sample[i] = MaxMod_Sample.Load(f)
			EndIf
		Next

		Return This
		
	EndFunction

' -----------------------------------------------------------------------------------------------------------

	Method Save(f:TStream)

		Local i:Int

		WriteByte f,Len(Name)
		WriteString f,Name
'		Print "saving instrument ~q"+Name+"~q"
		
		WriteShort f,SampleCount
		For i=0 Until SampleCount ; WriteShort f,NoteSample[i] ; Next

		WriteInt f,VolType
		WriteInt f,VolPoints
		WriteInt f,VolEnvelope.length
		For i=0 Until VolEnvelope.length ; WriteInt f,VolEnvelope[i] ; Next
		WriteInt f,VolSustain
		WriteInt f,VolFadeout
		WriteInt f,VolLoopStart
		WriteInt f,VolLoopEnd
		
		WriteInt f,CountList(VolEnvList)
		Local ep:MaxMod_EnvelopePoint
		For ep=EachIn VolEnvList
			ep.Save(f)
		Next

'		WriteInt f,VolSustainPoint
'		WriteInt f,VolLoopStartPoint
'		WriteInt f,VolLoopEndPoint
		
		WriteInt f,PanType
		WriteInt f,PanPoints
		WriteInt f,PanEnvelope.length
		For i=0 Until PanEnvelope.length ; WriteInt f,PanEnvelope[i] ; Next
		WriteInt f,PanSustain
		WriteInt f,PanLoopStart
		WriteInt f,PanLoopEnd
		
		WriteInt f,VibType
		WriteInt f,VibSweep
		WriteInt f,VibDepth
		WriteInt f,VibRate
		
		WriteInt f,Reserved
		
		WriteInt f,Sample.length
		For i=0 Until Sample.length
			If Sample[i]
				WriteByte f,1
				Sample[i].Save(f)
			Else
				WriteByte f,0
			EndIf
		Next

	EndMethod

' -----------------------------------------------------------------------------------------------------------

	Method GetSample:MaxMod_Sample(Note:Int)
		If Note>0 And Note<96
			Return Sample[NoteSample[Note]]
		Else
			Return Null
		EndIf
	EndMethod

' -----------------------------------------------------------------------------------------------------------

	Method AddVolumeEnvelopePoint(Tick:Int,Vol:Int)
		Local Point:MaxMod_EnvelopePoint = New MaxMod_EnvelopePoint
		Point.Tick 			= Tick
		Point.Value 		= Vol/100.0
		VolEnvList.AddLast(Point)
		VolEnvArray 		= VolEnvList.ToArray()	
	EndMethod

	Method SetVolSustainPoint(PointIndex:Int)
		If PointIndex>VolEnvArray.Length Then PointIndex=VolEnvArray.Length
		If PointIndex>0 Then PointIndex:-1
		VolSustainPoint 	= MaxMod_EnvelopePoint(VolEnvArray[PointIndex])
		VolSustain 			= PointIndex
	EndMethod

	Method SetVolLoopPoints(StartIndex:Int,EndIndex:Int)
		If StartIndex>VolEnvArray.Length Then StartIndex=VolEnvArray.Length
		If StartIndex>0 Then StartIndex:-1
		If EndIndex>VolEnvArray.Length Then EndIndex=VolEnvArray.Length
		If EndIndex>0 Then EndIndex:-1
		VolLoopStartPoint 	= MaxMod_EnvelopePoint(VolEnvArray[StartIndex])
		VolLoopStart 		= StartIndex
		VolLoopEndPoint 	= MaxMod_EnvelopePoint(VolEnvArray[EndIndex])
		VolLoopEnd 			= EndIndex
	EndMethod

' -----------------------------------------------------------------------------------------------------------

	Field Point:MaxMod_EnvelopePoint 
	Field NextPoint:MaxMod_EnvelopePoint 
	Field stp#

	Method GetEnvelopeVolume#(Tick:Int Var,EnvIndex:Int Var)
		If VolType=0 Or Volpoints=0 Then Return 1

		If VolType&%100 ; If EnvIndex=>VolLoopEnd Then tick = VolLoopStartPoint.Tick ; EnvIndex=VolLoopStart
		If VolType&%10  ; If EnvIndex=>VolSustain Then Return VolSustainPoint.value
		If EnvIndex>VolPoints Then EnvIndex=VolPoints
		If EnvIndex<0 Then EnvIndex=0
		
		Point = MaxMod_EnvelopePoint(VolEnvArray[EnvIndex])

		If Tick = Point.Tick Then Return Point.Value

		If EnvIndex+1=>VolPoints Then Return Point.Value
		NextPoint = MaxMod_EnvelopePoint(VolEnvArray[EnvIndex+1])

		If Tick => NextPoint.Tick Then EnvIndex:+1 ; Return NextPoint.Value
		
		If Tick > Point.Tick And Tick < NextPoint.Tick
			stp# = (NextPoint.Value - Point.Value) / Float(NextPoint.Tick-Point.Tick)
			Local result# = Point.Value + (stp*(tick-Point.Tick))
			If result>1 Then Return 1
			Return result
		EndIf

	EndMethod

EndType


Type MaxMod_EnvelopePoint

	Field Tick:Int
	Field Value#
	
	Function Load:MaxMod_EnvelopePoint(f:TStream)
		Local This:MaxMod_EnvelopePoint = New MaxMod_EnvelopePoint
		This.Tick = ReadByte(f)
		This.Value = ReadFloat(f)
		Return This
	EndFunction
	
	Method Save(f:TStream)
		WriteByte f,Tick
		WriteFloat f,Value
	EndMethod
	
EndType





